/****************************************************************************
 *  Copyright:    (C) 2005 - 2017 by Dipl.-Ing. Stefan Heesch
 *                callsign: HB9TWS
 *                email:    radio@heesch.net
 ****************************************************************************/

#include "morse.h"

static int   DurationPoint;
static char  RawBuffer[PATTERN_SIZE + 1];
static char  FailedBuffer[PATTERN_SIZE + 1];


const morse(MorseCode[]) PROGMEM =
  {
   { 'A',".-" }, { 'B',"-..." }, { 'C',"-.-." }, { 'D',"-.." },
   { 'E',"." }, { 'F',"..-." }, { 'G',"--." }, { 'H',"...." },
   { 'I',".." }, { 'J',".---" }, { 'K',"-.-" }, { 'L',".-.." },
   { 'M',"--" }, { 'N',"-." }, { 'O',"---" }, { 'P',".--." },
   { 'Q',"--.-" }, { 'R',".-." }, { 'S',"..." }, { 'T',"-" },
   { 'U',"..-" }, { 'V',"...-" }, { 'W',".--" }, { 'X',"-..-" },
   { 'Y',"-.--" }, { 'Z',"--.." },

   { '1',".----" }, { '2',"..---" }, { '3',"...--" }, { '4',"....-" },
   { '5',"....." }, { '6',"-...." }, { '7',"--..." }, { '8',"---.." },
   { '9',"----." }, { '0',"-----" },

   { ',',"--..--" }, { '.',".-.-.-" }, { '?',"..--.." }, { '/',"-..-." },
   { '=',"-...-" }, { '-',"-....-" }, { ':',"---..." }, { ';',"-.-.-." },
   { '(',"-.--." }, { ')',"-.--.-" }, { '\'',".----." },
   { '\"',".-..-." }, { '@',".--.-." }, { '#',"........" },

   { 'k',"-.-.-" },  // KA
   { 'a',".-.-." },  // AR
   { 's',"...-.-" }, // SK

   { '\0',"\0" }
  };


/* ----------------------------------------------------------------------
 * Function Name: pgm_read_morse( morse* in, morse* out )
 * Description:   Read cw pattern from program memore
 * Parameters:    morse* in, morse* out
 * Return:        void
 * ---------------------------------------------------------------------- */

void pgm_read_morse(morse* in, morse* out)
{
  out->letter = pgm_read_byte((PGM_P)in);

  for (int i = 0; i<PATTERN_SIZE; i++)
    out->pattern[i] = pgm_read_byte((PGM_P)in + i + 1);
}


/* ----------------------------------------------------------------------
 * Function Name: decode_letter( void )
 * Description:   Decode cw pattern in variable RawBuffer and return a char
 * Parameters:    void
 * Return:        char; =0
 * ---------------------------------------------------------------------- */

char decode_letter(void)
{
  if (RawBuffer[0] == 0) return 0;

  char result = DECODING_ERROR;
  morse code;
  int i = 0;

  do
    {
      pgm_read_morse(const_cast<morse*>(&MorseCode[i]), &code);
      if (strcmp(RawBuffer, code.pattern) == 0)
	{
	  result = code.letter;
	  break;
	}
      i++;
    } while (code.letter);

  if (result == DECODING_ERROR)
    memcpy(FailedBuffer, RawBuffer, PATTERN_SIZE);

  return result;
}


  /****************************************************************************
   *
   * Function Name: decode_tone( int duration )
   *
   * Description:   Decode cw tone ( key pressed )
   *
   * Parameters:    int duration
   *
   * Return:        void
   *
   ***************************************************************************/
void decode_tone(int duration)
{
  char s = '.';
  if (duration > 2 * DurationPoint)
    {
      s = '-';
    }

  int i;
  for (i = 0; i < PATTERN_SIZE; ++i)
    {
      if (RawBuffer[i] == 0)
	{
	  RawBuffer[i] = s;
	  break;
	}
    }

} /* decode_tone */


/* ----------------------------------------------------------------------
 * Function Name: decode_pause( int duration )
 * Description:   Decode cw pause ( key not pressed )
 * Parameters:    int duration
 * Return:        char
 * ---------------------------------------------------------------------- */

char decode_pause(int duration)
{
  char result = 0;

  if (duration > 2 * DurationPoint)
    {
      /* letter is complete, decode it and initialise buffer */
      result = decode_letter();
      memset(RawBuffer, 0, PATTERN_SIZE);
    }

  return result;
}


/* ----------------------------------------------------------------------
 * Function Name: morse_decode( cw input )
 * Description:   Decode cw input
 * Parameters:    cw input
 * Return:        char
 * ---------------------------------------------------------------------- */

char morse_decode(cw input)
{
  char result = 0;

  if (input.level == 0)
    result = decode_pause(input.duration);
  else
    decode_tone(input.duration);

  return result;
}


  /****************************************************************************
   *
   * Function Name: morse_speed( void )
   *
   * Description:   speed in letters per minute
   *
   * Parameters:    void
   *
   * Return:        int
   *
   ***************************************************************************/
int morse_speed(void)
{
  return  6000 / DurationPoint;

} /* morse_speed */


  /****************************************************************************
   *
   * Function Name: morse_timeout( void )
   *
   * Description:   return timeout value for decoding
   *
   * Parameters:    void
   *
   * Return:        int
   *
   ***************************************************************************/
int morse_timeout(void)
{
  return 8 * DurationPoint;

} /* morse_timeout */


  /****************************************************************************
   *
   * Function Name: morse_init( int speed )
   *
   * Description:   Initialize morse decoder
   *
   * Parameters:    int speed
   *
   * Return:        void
   *
   ***************************************************************************/
void morse_init(int speed)
{
  memset(RawBuffer, 0, PATTERN_SIZE + 1);
  memset(FailedBuffer, 0, PATTERN_SIZE + 1);


  if (speed > 0 && speed < 120)
    {
      DurationPoint = 6000 / speed;
    }
  else
    {
      DurationPoint = 6000 / 50;
    }

} /* morse_init */

  /****************************************************************************
   *
   * Function Name: morse_check( cw input )
   *
   * Description:   Check for speed and new word (spacing)
   *
   * Parameters:    cw input
   *
   * Return:        char
   *
   ***************************************************************************/
char morse_check(cw input)
{
  char result = 0;

  if (input.level == 0)
    {
      if (input.duration > 6 * DurationPoint)
	{
	  /* start a new block or word
	   */
	  result = ' ';
	}
    }
  else
    {
      /* Adopt speed
       */
      if (input.duration > 3 * DurationPoint + 15)
	{
	  DurationPoint += 5;
	}
      else if (input.duration < DurationPoint - 10)
	{
	  DurationPoint -= 5;
	}

      if (DurationPoint < MIN_DURATION)
	{
	  DurationPoint = MIN_DURATION;
	}
    }
  return result;

} /* morse_check */


  /****************************************************************************
   *
   * Function Name: morse_failed( void )
   *
   * Description:   Return pattern that couldn't be decoded
   *
   * Parameters:    void
   *
   * Return:        char*
   *
   ***************************************************************************/
char* morse_failed(void)
{
  return FailedBuffer;

} /* morse_failed */
